--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
tests/backend/test_auto_resend_regression.py 3f7ef731b5effd9aa3a81eceea66fd6c2b56498c (3f7ef731) Text, 10.55 KB
T8b949e# SPDX-License-Identifier: 0BSD
T8b949e"""Regression tests for auto-resend flooding guards and duplicate cleanup."""
Tff7b72from T7ee787__future__ Tff7b72import Te6edf3annotations
Tff7b72import T7ee787asyncio
Tff7b72import T7ee787json
Tff7b72import T7ee787time
Tff7b72import T7ee787types
Tff7b72from T7ee787unittestT7ee787.T7ee787mock Tff7b72import Te6edf3AsyncMockTb4b4b4, Te6edf3MagicMock
Tff7b72import T7ee787pytest
Tff7b72from T7ee787meshchatxT7ee787.T7ee787meshchat Tff7b72import Te6edf3ReticulumMeshChat
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backend Tff7b72import Te6edf3auto_resend_guard Tff7b72as Te6edf3guard
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787database Tff7b72import Te6edf3Database
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787databaseT7ee787.T7ee787provider Tff7b72import Te6edf3DatabaseProvider
Tff7b72from T7ee787meshchatxT7ee787.T7ee787srcT7ee787.T7ee787backendT7ee787.T7ee787databaseT7ee787.T7ee787schema Tff7b72import Te6edf3DatabaseSchema
Tf0883e@pytestTff7b72.Td2a8fffixture
Tff7b72def Td2a8ffdbTb4b4b4(Te6edf3tmp_pathTb4b4b4)Tb4b4b4:
Te6edf3path Tff7b72= Tffa657strTb4b4b4(Te6edf3tmp_path Tff7b72/ Ta5d6ff"Ta5d6ffresend_reg.dbTa5d6ff"Tb4b4b4)
Te6edf3provider Tff7b72= Te6edf3DatabaseProviderTb4b4b4(Te6edf3pathTb4b4b4)
Te6edf3DatabaseSchemaTb4b4b4(Te6edf3providerTb4b4b4)Tff7b72.Td2a8ffinitializeTb4b4b4(Tb4b4b4)
Te6edf3database Tff7b72= Te6edf3DatabaseTb4b4b4(Te6edf3pathTb4b4b4)
Tff7b72yield Te6edf3database
Te6edf3databaseTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Te6edf3providerTff7b72.Td2a8ffclose_allTb4b4b4(Tb4b4b4)
Tff7b72def Td2a8ff_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Tff7b72*Tb4b4b4, Te6edf3msg_hashTb4b4b4, Te6edf3peerTb4b4b4, Te6edf3contentTb4b4b4, Te6edf3fieldsTff7b72=Ta5d6ff"Tffd700{}Ta5d6ff"Tb4b4b4, Te6edf3tsTff7b72=Tff7b72NoneTb4b4b4, Te6edf3next_atTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Te6edf3now Tff7b72= Te6edf3ts Tff7b72if Te6edf3ts Tff7b72is Tff7b72not Tff7b72None Tff7b72else Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupsert_lxmf_messageTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4: Te6edf3msg_hashTb4b4b4,
Ta5d6ff"Ta5d6ffsource_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6fffailedTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffis_incomingTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffmethodTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffopportunisticTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdelivery_attemptsTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4: Te6edf3next_atTb4b4b4,
Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4: Te6edf3contentTb4b4b4,
Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4: Te6edf3fieldsTb4b4b4,
Ta5d6ff"Ta5d6ffrssiTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffsnrTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffqualityTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffis_spamTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffattachments_strippedTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: Te6edf3nowTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8ff_insert_outboundTb4b4b4(Te6edf3dbTb4b4b4, Tff7b72*Tb4b4b4, Te6edf3msg_hashTb4b4b4, Te6edf3peerTb4b4b4, Te6edf3contentTb4b4b4, Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffdeliveredTa5d6ff"Tb4b4b4, Te6edf3tsTff7b72=Tff7b72NoneTb4b4b4)Tb4b4b4:
Te6edf3now Tff7b72= Te6edf3ts Tff7b72if Te6edf3ts Tff7b72is Tff7b72not Tff7b72None Tff7b72else Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffupsert_lxmf_messageTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4: Te6edf3msg_hashTb4b4b4,
Ta5d6ff"Ta5d6ffsource_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffdestination_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffpeer_hashTa5d6ff"Tb4b4b4: Te6edf3peerTb4b4b4,
Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4: Te6edf3stateTb4b4b4,
Ta5d6ff"Ta5d6ffprogressTa5d6ff"Tb4b4b4: T79c0ff1Tb4b4b4,
Ta5d6ff"Ta5d6ffis_incomingTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffmethodTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffdirectTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffdelivery_attemptsTa5d6ff"Tb4b4b4: T79c0ff1Tb4b4b4,
Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6fftitleTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffcontentTa5d6ff"Tb4b4b4: Te6edf3contentTb4b4b4,
Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4: Ta5d6ff"Tffd700{}Ta5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffrssiTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffsnrTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffqualityTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffis_spamTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6ffreply_to_hashTa5d6ff"Tb4b4b4: Tff7b72NoneTb4b4b4,
Ta5d6ff"Ta5d6ffattachments_strippedTa5d6ff"Tb4b4b4: T79c0ff0Tb4b4b4,
Ta5d6ff"Ta5d6fftimestampTa5d6ff"Tb4b4b4: Te6edf3nowTb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Tff7b72def Td2a8ff_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3app Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ff_auto_resend_coordinator Tff7b72= Te6edf3guardTff7b72.Td2a8ffAutoResendCoordinatorTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffwebsocket_broadcast Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffsend_message Tff7b72= Te6edf3AsyncMockTb4b4b4(Tb4b4b4)
Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destination Tff7b72= Te6edf3typesTff7b72.Td2a8ffMethodTypeTb4b4b4(
Te6edf3ReticulumMeshChatTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4,
Te6edf3appTb4b4b4,
Tb4b4b4)
Te6edf3ctx Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3ctxTff7b72.Td2a8ffidentityTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTff7b72.Td2a8ffreturn_value Tff7b72= Ta5d6ff"Ta5d6ffaaTa5d6ff" Tff7b72* T79c0ff16
Te6edf3ctxTff7b72.Td2a8ffdatabase Tff7b72= Te6edf3db
Te6edf3ctxTff7b72.Td2a8ffconfigTff7b72.Td2a8ffallow_auto_resending_failed_messages_with_attachmentsTff7b72.Td2a8ffgetTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72True
Tff7b72return Te6edf3appTb4b4b4, Te6edf3ctx
Tff7b72def Td2a8fftest_fields_have_attachments_and_invalid_jsonTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72assert Tff7b72not Te6edf3guardTff7b72.Td2a8fffields_have_attachmentsTb4b4b4(Ta5d6ff"Tffd700{}Ta5d6ff"Tb4b4b4)
Tff7b72assert Tff7b72not Te6edf3guardTff7b72.Td2a8fffields_have_attachmentsTb4b4b4(Ta5d6ff"Ta5d6ffnot-jsonTa5d6ff"Tb4b4b4)
Tff7b72assert Te6edf3guardTff7b72.Td2a8fffields_have_attachmentsTb4b4b4(Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6ffimageTa5d6ff"Ta5d6ff:Ta5d6ff{Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Ta5d6ff:Ta5d6ff"Ta5d6ffpngTa5d6ff"Ta5d6ff}}Ta5d6ff'Tb4b4b4)
Tff7b72assert Te6edf3guardTff7b72.Td2a8fffields_have_attachmentsTb4b4b4(Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6fffile_attachmentsTa5d6ff"Ta5d6ff:[Ta5d6ff{Ta5d6ff"Ta5d6fffile_nameTa5d6ff"Ta5d6ff:Ta5d6ff"Ta5d6ffaTa5d6ff"Ta5d6ff}]}Ta5d6ff'Tb4b4b4)
Tff7b72assert Te6edf3guardTff7b72.Td2a8ffparse_fields_dictTb4b4b4(Ta5d6ff"Ta5d6ffnot-jsonTa5d6ff"Tb4b4b4) Tff7b72== Tb4b4b4{Tb4b4b4}
Tff7b72assert Te6edf3guardTff7b72.Td2a8ffshould_skip_for_budgetTb4b4b4(
Ta5d6ff'Ta5d6ff{Ta5d6ff"Ta5d6ff_mcx_auto_resend_countTa5d6ff"Ta5d6ff: 3}Ta5d6ff'Tb4b4b4,
Te6edf3max_attemptsTff7b72=T79c0ff3Tb4b4b4,
Tb4b4b4)
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_skips_when_budget_exhaustedTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ffbTa5d6ff" Tff7b72* T79c0ff32
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffcTa5d6ff" Tff7b72* T79c0ff32
Te6edf3_insert_failedTb4b4b4(
Te6edf3dbTb4b4b4,
Te6edf3msg_hashTff7b72=Te6edf3msgTb4b4b4,
Te6edf3peerTff7b72=Te6edf3peerTb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffhelpTa5d6ff"Tb4b4b4,
Te6edf3fieldsTff7b72=Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(Tb4b4b4{Ta5d6ff"Ta5d6ff_mcx_auto_resend_countTa5d6ff"Tb4b4b4: T79c0ff3Tb4b4b4}Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Tff7b72await Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffassert_not_calledTb4b4b4(Tb4b4b4)
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_skips_when_recent_same_content_existsTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ffdTa5d6ff" Tff7b72* T79c0ff32
Te6edf3now Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
Te6edf3_insert_outboundTb4b4b4(
Te6edf3dbTb4b4b4,
Te6edf3msg_hashTff7b72=Ta5d6ff"Ta5d6ff1Ta5d6ff" Tff7b72* T79c0ff32Tb4b4b4,
Te6edf3peerTff7b72=Te6edf3peerTb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffhelp textTa5d6ff"Tb4b4b4,
Te6edf3tsTff7b72=Te6edf3now Tff7b72- T79c0ff30Tb4b4b4,
Tb4b4b4)
Te6edf3_insert_failedTb4b4b4(
Te6edf3dbTb4b4b4,
Te6edf3msg_hashTff7b72=Ta5d6ff"Ta5d6ff2Ta5d6ff" Tff7b72* T79c0ff32Tb4b4b4,
Te6edf3peerTff7b72=Te6edf3peerTb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffhelp textTa5d6ff"Tb4b4b4,
Te6edf3tsTff7b72=Te6edf3now Tff7b72- T79c0ff5Tb4b4b4,
Tb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Tff7b72await Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffassert_not_calledTb4b4b4(Tb4b4b4)
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_skips_attachments_before_claimTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ffeTa5d6ff" Tff7b72* T79c0ff32
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6fffTa5d6ff" Tff7b72* T79c0ff32
Te6edf3fields Tff7b72= Te6edf3jsonTff7b72.Td2a8ffdumpsTb4b4b4(
Tb4b4b4{
Ta5d6ff"Ta5d6ffimageTa5d6ff"Tb4b4b4: Tb4b4b4{
Ta5d6ff"Ta5d6ffimage_typeTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffpngTa5d6ff"Tb4b4b4,
Ta5d6ff"Ta5d6ffimage_bytesTa5d6ff"Tb4b4b4: Ta5d6ff"Ta5d6ffaaTa5d6ff"Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4}Tb4b4b4,
Tb4b4b4)
Te6edf3_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Te6edf3msgTb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffpicTa5d6ff"Tb4b4b4, Te6edf3fieldsTff7b72=Te6edf3fieldsTb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Te6edf3ctxTff7b72.Td2a8ffconfigTff7b72.Td2a8ffallow_auto_resending_failed_messages_with_attachmentsTff7b72.Td2a8ffgetTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72False
Tff7b72await Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffassert_not_calledTb4b4b4(Tb4b4b4)
Te6edf3row Tff7b72= Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT next_delivery_attempt_at FROM lxmf_messages WHERE hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3msgTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
T8b949e# Must not burn cooldown when attachments are disallowed.
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4] Tff7b72is Tff7b72None
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_claims_once_and_deletes_old_on_successTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ff1Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3old_hash Tff7b72= Ta5d6ff"Ta5d6ff2Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3new_hash_bytes Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Ta5d6ff"Ta5d6ff3Ta5d6ff" Tff7b72* T79c0ff32Tb4b4b4)
Te6edf3_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Te6edf3old_hashTb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffretry meTa5d6ff"Tb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Tff7b72async Tff7b72def Td2a8ffsend_and_materializeTb4b4b4(Tff7b72*Te6edf3argsTb4b4b4, Tff7b72*Tff7b72*Te6edf3kwargsTb4b4b4)Tb4b4b4:
Te6edf3new_msg Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3new_msgTff7b72.Td2a8ffhash Tff7b72= Te6edf3new_hash_bytes
Te6edf3_insert_outboundTb4b4b4(
Te6edf3dbTb4b4b4,
Te6edf3msg_hashTff7b72=Te6edf3new_hash_bytesTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3peerTff7b72=Te6edf3peerTb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffretry meTa5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffoutboundTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3new_msg
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffside_effect Tff7b72= Te6edf3send_and_materialize
Tff7b72await Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)
Tff7b72assert Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffawait_count Tff7b72== T79c0ff1
Tff7b72assert Tb4b4b4(
Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT 1 FROM lxmf_messages WHERE hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3old_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72is Tff7b72None
Tb4b4b4)
Te6edf3new_row Tff7b72= Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT fields FROM lxmf_messages WHERE hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3new_hash_bytesTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72assert Te6edf3guardTff7b72.Td2a8ffread_auto_resend_countTb4b4b4(Te6edf3new_rowTb4b4b4[Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72== T79c0ff1
Te6edf3appTff7b72.Td2a8ffwebsocket_broadcastTff7b72.Td2a8ffassert_awaitedTb4b4b4(Tb4b4b4)
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_concurrent_calls_send_only_onceTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ff4Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3old_hash Tff7b72= Ta5d6ff"Ta5d6ff5Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Te6edf3old_hashTb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffraceTa5d6ff"Tb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Tff7b72async Tff7b72def Td2a8ffslow_sendTb4b4b4(Tff7b72*Te6edf3argsTb4b4b4, Tff7b72*Tff7b72*Te6edf3kwargsTb4b4b4)Tb4b4b4:
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffsleepTb4b4b4(T79c0ff0.05Tb4b4b4)
Te6edf3m Tff7b72= Te6edf3MagicMockTb4b4b4(Tb4b4b4)
Te6edf3mTff7b72.Td2a8ffhash Tff7b72= Tffa657bytesTff7b72.Td2a8fffromhexTb4b4b4(Ta5d6ff"Ta5d6ff6Ta5d6ff" Tff7b72* T79c0ff32Tb4b4b4)
Te6edf3_insert_outboundTb4b4b4(
Te6edf3dbTb4b4b4,
Te6edf3msg_hashTff7b72=Te6edf3mTff7b72.Td2a8ffhashTff7b72.Td2a8ffhexTb4b4b4(Tb4b4b4)Tb4b4b4,
Te6edf3peerTff7b72=Te6edf3peerTb4b4b4,
Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffraceTa5d6ff"Tb4b4b4,
Te6edf3stateTff7b72=Ta5d6ff"Ta5d6ffoutboundTa5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72return Te6edf3m
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffside_effect Tff7b72= Te6edf3slow_send
Tff7b72await Te6edf3asyncioTff7b72.Td2a8ffgatherTb4b4b4(
Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)Tb4b4b4,
Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72assert Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffawait_count Tff7b72== T79c0ff1
Tf0883e@pytestTff7b72.Td2a8ffmarkTff7b72.Td2a8ffasyncio
Tff7b72async Tff7b72def Td2a8fftest_resend_does_not_delete_old_when_send_returns_noneTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ff7Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3old_hash Tff7b72= Ta5d6ff"Ta5d6ff8Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Te6edf3old_hashTb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffkeepTa5d6ff"Tb4b4b4)
Te6edf3appTb4b4b4, Te6edf3ctx Tff7b72= Te6edf3_bind_resend_appTb4b4b4(Te6edf3dbTb4b4b4)
Te6edf3appTff7b72.Td2a8ffsend_messageTff7b72.Td2a8ffreturn_value Tff7b72= Tff7b72None
Tff7b72await Te6edf3appTff7b72.Td2a8ffresend_failed_messages_for_destinationTb4b4b4(Te6edf3peerTb4b4b4, Te6edf3contextTff7b72=Te6edf3ctxTb4b4b4)
Tff7b72assert Tb4b4b4(
Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT state FROM lxmf_messages WHERE hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3old_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)Tb4b4b4[Ta5d6ff"Ta5d6ffstateTa5d6ff"Tb4b4b4]
Tff7b72== Ta5d6ff"Ta5d6fffailedTa5d6ff"
Tb4b4b4)
T8b949e# Cooldown claimed, attempt counted.
Te6edf3row Tff7b72= Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT fields, next_delivery_attempt_at FROM lxmf_messages WHERE hash = ?Ta5d6ff"Tb4b4b4,
Tb4b4b4(Te6edf3old_hashTb4b4b4,Tb4b4b4)Tb4b4b4,
Tb4b4b4)
Tff7b72assert Te6edf3guardTff7b72.Td2a8ffread_auto_resend_countTb4b4b4(Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6fffieldsTa5d6ff"Tb4b4b4]Tb4b4b4) Tff7b72== T79c0ff1
Tff7b72assert Te6edf3rowTb4b4b4[Ta5d6ff"Ta5d6ffnext_delivery_attempt_atTa5d6ff"Tb4b4b4] Tff7b72is Tff7b72not Tff7b72None
Tff7b72def Td2a8fftest_duplicate_cleanup_keeps_oldest_by_timestampTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ff9Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3base Tff7b72= Te6edf3timeTff7b72.Td2a8fftimeTb4b4b4(Tb4b4b4)
T8b949e# Later insert id can still be oldest by timestamp.
Te6edf3_insert_outboundTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Ta5d6ff"Ta5d6ffaTa5d6ff" Tff7b72* T79c0ff32Tb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffdupTa5d6ff"Tb4b4b4, Te6edf3tsTff7b72=Te6edf3base Tff7b72- T79c0ff5Tb4b4b4)
Te6edf3_insert_outboundTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Ta5d6ff"Ta5d6ffbTa5d6ff" Tff7b72* T79c0ff32Tb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffdupTa5d6ff"Tb4b4b4, Te6edf3tsTff7b72=Te6edf3base Tff7b72- T79c0ff50Tb4b4b4)
Te6edf3_insert_outboundTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Ta5d6ff"Ta5d6ffcTa5d6ff" Tff7b72* T79c0ff32Tb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffdupTa5d6ff"Tb4b4b4, Te6edf3tsTff7b72=Te6edf3base Tff7b72- T79c0ff1Tb4b4b4)
Te6edf3deleted Tff7b72= Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8ffdelete_duplicate_lxmf_messages_by_contentTb4b4b4(Tb4b4b4)
Tff7b72assert Te6edf3deleted Tff7b72== T79c0ff2
Te6edf3left Tff7b72= Te6edf3dbTff7b72.Td2a8ffproviderTff7b72.Td2a8fffetchoneTb4b4b4(
Ta5d6ff"Ta5d6ffSELECT hash FROM lxmf_messages WHERE content = Ta5d6ff'Ta5d6ffdupTa5d6ff'Ta5d6ff"Tb4b4b4,
Tb4b4b4)
Tff7b72assert Te6edf3leftTb4b4b4[Ta5d6ff"Ta5d6ffhashTa5d6ff"Tb4b4b4] Tff7b72== Ta5d6ff"Ta5d6ffbTa5d6ff" Tff7b72* T79c0ff32
Tff7b72def Td2a8fftest_source_keeps_auto_resend_and_duplicate_guardsTb4b4b4(Tb4b4b4)Tb4b4b4:
Tff7b72from T7ee787pathlib Tff7b72import Te6edf3Path
Te6edf3root Tff7b72= Te6edf3PathTb4b4b4(Tff7b72__file__Tb4b4b4)Tff7b72.Td2a8ffresolveTb4b4b4(Tb4b4b4)Tff7b72.Td2a8ffparentsTb4b4b4[T79c0ff2Tb4b4b4]
Te6edf3meshchat Tff7b72= Tb4b4b4(Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffmeshchat.pyTa5d6ff"Tb4b4b4)Tff7b72.Td2a8ffread_textTb4b4b4(Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3guard_src Tff7b72= Tb4b4b4(
Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffsrcTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffbackendTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffauto_resend_guard.pyTa5d6ff"
Tb4b4b4)Tff7b72.Td2a8ffread_textTb4b4b4(Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3messages Tff7b72= Tb4b4b4(
Te6edf3root Tff7b72/ Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffsrcTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffbackendTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffdatabaseTa5d6ff" Tff7b72/ Ta5d6ff"Ta5d6ffmessages.pyTa5d6ff"
Tb4b4b4)Tff7b72.Td2a8ffread_textTb4b4b4(Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Te6edf3settings Tff7b72= Tb4b4b4(
Te6edf3root
Tff7b72/ Ta5d6ff"Ta5d6ffmeshchatxTa5d6ff"
Tff7b72/ Ta5d6ff"Ta5d6ffsrcTa5d6ff"
Tff7b72/ Ta5d6ff"Ta5d6fffrontendTa5d6ff"
Tff7b72/ Ta5d6ff"Ta5d6ffcomponentsTa5d6ff"
Tff7b72/ Ta5d6ff"Ta5d6ffsettingsTa5d6ff"
Tff7b72/ Ta5d6ff"Ta5d6ffSettingsPage.vueTa5d6ff"
Tb4b4b4)Tff7b72.Td2a8ffread_textTb4b4b4(Te6edf3encodingTff7b72=Ta5d6ff"Ta5d6ffutf-8Ta5d6ff"Tb4b4b4)
Tff7b72assert Ta5d6ff"Ta5d6ffAutoResendCoordinatorTa5d6ff" Tff7b72in Te6edf3meshchat
Tff7b72assert Ta5d6ff"Ta5d6fftry_claim_failed_message_for_auto_resendTa5d6ff" Tff7b72in Te6edf3meshchat
Tff7b72assert Ta5d6ff"Ta5d6fffields_have_attachmentsTa5d6ff" Tff7b72in Te6edf3meshchat
Tff7b72assert Ta5d6ff"Ta5d6ffhas_path(destination_hash)Ta5d6ff" Tff7b72in Te6edf3meshchat
Tff7b72assert Ta5d6ff"Ta5d6ffMAX_AUTO_RESEND_ATTEMPTSTa5d6ff" Tff7b72in Te6edf3guard_src
Tff7b72assert Ta5d6ff"Ta5d6ffdelete_duplicate_lxmf_messages_by_contentTa5d6ff" Tff7b72in Te6edf3messages
Tff7b72assert Ta5d6ff"Ta5d6ffclearDuplicateMessagesTa5d6ff" Tff7b72in Te6edf3settings
Tff7b72assert Ta5d6ff"Ta5d6ff/api/v1/maintenance/messages/duplicatesTa5d6ff" Tff7b72in Te6edf3meshchat
Tff7b72def Td2a8fftest_claim_reopens_after_cooldown_expiresTb4b4b4(Te6edf3dbTb4b4b4)Tb4b4b4:
Te6edf3peer Tff7b72= Ta5d6ff"Ta5d6ff0Ta5d6ff" Tff7b72* T79c0ff32
Te6edf3msg Tff7b72= Ta5d6ff"Ta5d6ffaTa5d6ff" Tff7b72* T79c0ff32
Te6edf3now Tff7b72= T79c0ff1_000_000.0
Te6edf3_insert_failedTb4b4b4(Te6edf3dbTb4b4b4, Te6edf3msg_hashTff7b72=Te6edf3msgTb4b4b4, Te6edf3peerTff7b72=Te6edf3peerTb4b4b4, Te6edf3contentTff7b72=Ta5d6ff"Ta5d6ffxTa5d6ff"Tb4b4b4, Te6edf3next_atTff7b72=Te6edf3now Tff7b72- T79c0ff1Tb4b4b4)
Tff7b72assert Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8fftry_claim_failed_message_for_auto_resendTb4b4b4(
Te6edf3msgTb4b4b4,
Te6edf3cooldown_untilTff7b72=Te6edf3now Tff7b72+ T79c0ff120Tb4b4b4,
Te6edf3nowTff7b72=Te6edf3nowTb4b4b4,
Tb4b4b4)
Tff7b72assert Tff7b72not Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8fftry_claim_failed_message_for_auto_resendTb4b4b4(
Te6edf3msgTb4b4b4,
Te6edf3cooldown_untilTff7b72=Te6edf3now Tff7b72+ T79c0ff240Tb4b4b4,
Te6edf3nowTff7b72=Te6edf3now Tff7b72+ T79c0ff10Tb4b4b4,
Tb4b4b4)
Tff7b72assert Te6edf3dbTff7b72.Td2a8ffmessagesTff7b72.Td2a8fftry_claim_failed_message_for_auto_resendTb4b4b4(
Te6edf3msgTb4b4b4,
Te6edf3cooldown_untilTff7b72=Te6edf3now Tff7b72+ T79c0ff360Tb4b4b4,
Te6edf3nowTff7b72=Te6edf3now Tff7b72+ T79c0ff121Tb4b4b4,
Tb4b4b4)
──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────